#include <glew.h>
#include <openglut.h>
#include <list>

//different types of surface patches
#define TYPE_BG  0    //not rendered
#define TYPE_LU  1    //16 triangles
#define TYPE_LL  2    //16 triangles
#define TYPE_RU  3    //16 triangles
#define TYPE_RL  4    //16 triangles
#define TYPE_DIV 5    //32 triangles
#define TYPE_OBJ 6    // 2 triangles

/////////////////////////////////////////////////////////////////////////////////////////////

struct Vec3
{
	float x, y, z;
	
	Vec3();
	~Vec3();
	//set x, y, z to 0;
	void reset();
	//v = v1 x v2
	void crossProd(Vec3 v1, Vec3 v2);
	//v = unit length
	void unitLength();
	//v = v1 - v2
	void diff(Vec3 v1, Vec3 v2);
	//v = v1 + v2
	void sum(Vec3 v1, Vec3 v2);
};

Vec3::Vec3()
{
	reset();
}

Vec3::~Vec3(){}

void Vec3::reset()
{
	x = 0; y = 0; z = 0;
}

void Vec3::crossProd(Vec3 v1, Vec3 v2)
{
	x = v1.y*v2.z - v1.z*v2.y;
	y = v1.x*v2.z - v1.z*v2.x;
    z = v1.x*v2.y - v1.y*v2.x;
}

void Vec3::unitLength()
{
	float l = sqrt(x*x+y*y+z*z);
	x = x/l;
	y = y/l;
	z = z/l;
}

void Vec3::diff(Vec3 v1, Vec3 v2)
{
	x = v1.x - v2.x;
	y = v1.y - v2.y;
    z = v1.z - v2.z;
}

void Vec3::sum(Vec3 v1, Vec3 v2)
{
	x = v1.x + v2.x;
	y = v1.y + v2.y;
    z = v1.z + v2.z;
}

/////////////////////////////////////////////////////////////////////////////////////////////

struct Vertex
{
	//position
	Vec3 pos;
	//vertex normal
	Vec3 normal;
	
	Vertex();
	~Vertex();
	//sets everything other that the x & y coordinate to 0
	void reset();
	//calls the normal vector (glNormal3f) if b is true, followed by the vertex coordinates (glVertex3f)
	void draw(bool b);
	//used for calculating the vertex normal
	void updateNormalVector(Vec3 v);
};

Vertex::Vertex()
{
	pos.reset();
	normal.reset();
}

Vertex::~Vertex(){}

void Vertex::reset()
{
	pos.z = 0.0f;
	normal.reset();
}

void Vertex::draw(bool b)
{
	if (b)
	{
	    normal.unitLength();
	    glNormal3f(normal.x, normal.y, normal.z);
	}
	glVertex3f(pos.x, pos.y, pos.z);
}

void Vertex::updateNormalVector(Vec3 v)
{
    normal.sum(v, normal);
}

/////////////////////////////////////////////////////////////////////////////////////////////

struct Triangle
{
	//vertices
	Vertex *p1, *p2, *p3;
	//surface normal
	Vec3 normal;

	Triangle();
	~Triangle();
	//draws the triangle, using the surface normal if b is true
	void draw(bool b);
	//calculates the surface normal off the triangle's vertices
	void calculateNormal();
};

Triangle::Triangle()
{
	normal.reset();
	p1 = NULL; p2 = NULL; p3 = NULL;
}

Triangle::~Triangle()
{
	p1 = NULL; p2 = NULL; p3 = NULL;
}

void Triangle::draw(bool b)
{
	if (b) 
	{
	    normal.unitLength();
		glNormal3f(normal.x, normal.y, normal.z);
	}
	//if surface normal is used, no vertex normals
	p1->draw(!b);
	p2->draw(!b);
	p3->draw(!b);
}

void Triangle::calculateNormal()
{
	Vec3 v1, v2;
	v1.diff(p2->pos, p1->pos);
	v2.diff(p2->pos, p3->pos);
	normal.crossProd(v1, v2);
}

/////////////////////////////////////////////////////////////////////////////////////////////

struct SurfacePatch
{
	//type of the surface patch
	int type;
	//pointers to the 25 vertices located on the surface patch
	Vertex *vertices[25];
	//triangle list
	std::list<Triangle> triangles;

	SurfacePatch();
	~SurfacePatch();
	//clears triangles & resets type
	void reset();
	//draws the surface patch, using the surface normal if b is true, using the vertex normals otherwise
	void draw(bool b);
	//creates a triangle off 3 vertex indices, and adds it to the list
	void addTriangle(int a, int b, int c);
	//creates triangles
	void createTriangleList();
};

SurfacePatch::SurfacePatch()
{
	type = 0;
	triangles.clear();
	for (int i=0; i<25; i++) vertices[i] = NULL;
}

SurfacePatch::~SurfacePatch()
{
	triangles.clear();
	for (int i=0; i<25; i++) vertices[i] = NULL;
}

void SurfacePatch::reset()
{
	type = 0;
	triangles.clear();
}

void SurfacePatch::draw(bool b)
{
	glBegin(GL_TRIANGLES);
	std::list<Triangle>::iterator i;
	for(i = triangles.begin(); i != triangles.end(); i++) i->draw(b);
	glEnd();
}

void SurfacePatch::addTriangle(int a, int b, int c)
{
	Triangle t;
	t.p1 = vertices[a];
	t.p2 = vertices[b];
	t.p3 = vertices[c];
	t.calculateNormal();
	triangles.push_back(t);
	vertices[a]->updateNormalVector(t.normal);
	vertices[b]->updateNormalVector(t.normal);
	vertices[c]->updateNormalVector(t.normal);
}

void SurfacePatch::createTriangleList()
{
	if (type == TYPE_OBJ)
	{
		addTriangle(20, 0, 24);
		addTriangle(0, 4, 24);
	}
	else if (type == TYPE_DIV)
	{
		int ind[96] = { 5,  0,  6,  0,  1,  6,  6,  1,  7,  1,  2,  7,  7,  2,  8,  2,  3,  8,  8,  3,  9,  3,  4,  9,
			            10,  5, 11,  5,  6, 11, 11,  6, 12,  6,  7, 12,  12, 7, 13,  7,  8, 13, 13,  8, 14,  8,  9, 14,
						15, 10, 16, 10, 11, 16, 16, 11, 17, 11, 12, 17, 17, 12, 18, 12, 13, 18, 18, 13, 19, 13, 14, 19,
						20, 15, 21, 15, 16, 21, 21, 16, 22, 16, 17, 22, 22, 17, 23, 17, 18, 23, 23, 18, 24, 18, 19, 24};

		for (int i=0; i<32; i=i++)
            addTriangle(ind[3*i], ind[3*i+1], ind[3*i+2]);
	}
	else if (type == TYPE_LU)
	{
		int ind[48] = { 5,  0,  6,  0,  1,  6,  6,  1,  7,  1,  2,  7,
			           10,  5, 11,  5,  6, 11, 11,  6, 12,  6,  7, 12,
					   14, 12,  7, 14,  7,  2, 14,  2,  4, 22, 20, 10,
					   22, 10, 11, 22, 11, 12, 24, 22, 12, 24, 12, 14};

		for (int i=0; i<16; i++)
            addTriangle(ind[3*i], ind[3*i+1], ind[3*i+2]);
	}
	else if (type == TYPE_LL)
	{
		int ind[48] = { 0, 11, 10,  0, 12, 11,  0,  2, 12, 12,  2, 14,
			            2,  4, 14, 15, 10, 16, 10, 11, 16, 16, 11, 17,
					   11, 12, 17, 20, 15, 21, 15, 16, 21, 21, 16, 22,
					   16, 17, 22, 24, 22, 17, 24, 17, 12, 24, 12, 14};

		for (int i=0; i<16; i++)
			addTriangle(ind[3*i], ind[3*i+1], ind[3*i+2]);
	}
	else if (type == TYPE_RU)
	{	
		int ind[48] = { 0, 12, 10,  0,  7, 12,  0,  2,  7,  7,  2,  8,
			            2,  3,  8,  8,  3,  9,  3,  4,  9, 12,  7, 13,
						7,  8, 13, 13,  8, 14,  8,  9, 14, 20, 10, 22, 
					   10, 12, 22, 24, 22, 12, 24, 12, 13, 24, 13, 14};

		for (int i=0; i<16; i++)
			addTriangle(ind[3*i], ind[3*i+1], ind[3*i+2]);
	}
	else if (type == TYPE_RL)
	{
		int ind[48] = {10,  0, 12,  0,  2, 12,  2, 13, 12,  2, 14, 13,
			            2,  4, 14, 10, 12, 17, 10, 17, 22, 10, 22, 20,
					   17, 12, 18, 12, 13, 18, 18, 13, 19, 13, 14, 19,
					   22, 17, 23, 17, 18, 23, 23, 18, 24, 18, 19, 24};

		for (int i=0; i<16; i++)
			addTriangle(ind[3*i], ind[3*i+1], ind[3*i+2]);
	}
}